package com.wsoft.bpm.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.wsoft.bpm.component.command.GetProcessCmd;
import com.wsoft.bpm.component.command.GetProcessDefinitionCacheEntryCmd;
import com.wsoft.bpm.component.sign.TaskModel;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.engine.ManagementService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.delegate.Expression;
import org.activiti.engine.delegate.VariableScope;
import org.activiti.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.activiti.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.activiti.engine.impl.bpmn.parser.factory.ActivityBehaviorFactory;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.el.ExpressionManager;
import org.activiti.engine.impl.persistence.deploy.ProcessDefinitionCacheEntry;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.task.Task;
import org.w3c.dom.Document;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static com.wsoft.bpm.constants.BpmConstant.*;

/**
 * 工作流工具类
 *
 * @author zhonglj
 * @date 2024-11-15 15:20
 **/
public class ActivityUtils {

    /**
     * 生成连线
     *
     * @param id
     * @param name
     * @param source
     * @param target
     * @return
     */
    public static SequenceFlow buildSequenceFlow(String id, String name, String source, String target) {
        SequenceFlow sequenceFlow = new SequenceFlow(source, target);
        sequenceFlow.setId(id);
        sequenceFlow.setName(name);
        return sequenceFlow;
    }

    public static SequenceFlow buildAndSetSequenceFlow(String processDefId, String source, String target) {
        ManagementService managementService = SpringUtil.getBean(ManagementService.class);
        // 通过缓存获取
        ProcessDefinitionCacheEntry processDefinitionCacheEntry = managementService.executeCommand(new GetProcessDefinitionCacheEntryCmd(processDefId));
        Process process = processDefinitionCacheEntry.getProcess();
        FlowNode curFlowNode = (FlowNode) process.getFlowElement(source);
        FlowNode targetFlowNode = (FlowNode) process.getFlowElement(target);
        SequenceFlow sequenceFlow = new SequenceFlow(curFlowNode.getId(), targetFlowNode.getId());
        sequenceFlow.setId(curFlowNode.getId() + "-->" + targetFlowNode.getId());
        sequenceFlow.setName(curFlowNode.getName() + "-->" + targetFlowNode.getName());
        sequenceFlow.setSourceFlowElement(curFlowNode);
        sequenceFlow.setTargetFlowElement(targetFlowNode);

        // 出口方向设置
        process.addFlowElement(sequenceFlow);
        curFlowNode.setOutgoingFlows(Arrays.asList(sequenceFlow));
        return sequenceFlow;
    }

    /**
     * 创建任务
     *
     * @param id
     * @param name
     * @param assignee
     * @param processEngine
     * @return
     */
    public static UserTask buildUserTask(String id, String name, String assignee, ProcessEngine processEngine) {
        UserTask userTask = new UserTask();
        userTask.setId(id);
        userTask.setName(name);
        userTask.setAssignee(assignee);
        userTask.setBehavior(createUserTaskBehavior(userTask, processEngine));
        return userTask;
    }

    private static UserTaskActivityBehavior createUserTaskBehavior(UserTask userTask, ProcessEngine processEngine) {
        ProcessEngineConfigurationImpl processEngineConfiguration = (ProcessEngineConfigurationImpl) processEngine.getProcessEngineConfiguration();
        ActivityBehaviorFactory activityBehaviorFactory = processEngineConfiguration.getActivityBehaviorFactory();
        return activityBehaviorFactory.createUserTaskActivityBehavior(userTask);
    }

    public static ParallelMultiInstanceBehavior createParallelMultiInstanceBehavior(UserTask userTask, ProcessEngine processEngine) {
        ProcessEngineConfigurationImpl processEngineConfiguration = (ProcessEngineConfigurationImpl) processEngine.getProcessEngineConfiguration();
        ActivityBehaviorFactory activityBehaviorFactory = processEngineConfiguration.getActivityBehaviorFactory();
        return activityBehaviorFactory.createParallelMultiInstanceBehavior(userTask, activityBehaviorFactory.createUserTaskActivityBehavior(userTask));
    }

    public static SequentialMultiInstanceBehavior createSequentialMultiInstanceBehavior(UserTask userTask, ProcessEngine processEngine) {
        ProcessEngineConfigurationImpl processEngineConfiguration = (ProcessEngineConfigurationImpl) processEngine.getProcessEngineConfiguration();
        ActivityBehaviorFactory activityBehaviorFactory = processEngineConfiguration.getActivityBehaviorFactory();
        return activityBehaviorFactory.createSequentialMultiInstanceBehavior(userTask, activityBehaviorFactory.createUserTaskActivityBehavior(userTask));
    }

    /**
     * 生成自定义任务模型
     *
     * @param id
     * @param name
     * @param assignee
     * @return
     */
    public static TaskModel buildTaskModel(String id, String name, String assignee) {
        TaskModel taskModel = new TaskModel();
        taskModel.setId(id);
        taskModel.setName(name);
        taskModel.setAssignee(assignee);
        return taskModel;
    }

    /**
     * 将自定义的 TaskModel
     *
     * @param taskModel
     * @param processEngine
     * @return
     */
    public static UserTask convertToUserTask(TaskModel taskModel, ProcessEngine processEngine) {
        return buildUserTask(taskModel.getId(), taskModel.getName(), taskModel.getAssignee(), processEngine);
    }

    /**
     * 获取当前节点每个方向上的第一个前置用户任务
     *
     * @param currentElement
     * @return void
     * @author zhonglj
     * @date 2024-10-14 17:06
     **/
    public static List<FlowElement> getPreFirstUserTask(FlowElement currentElement) {
        List<FlowElement> userTasks = new ArrayList<>();
        FlowNode currentNode = (FlowNode) currentElement;
        // 获取当前节点的所有入线
        List<SequenceFlow> incomingFlows = currentNode.getIncomingFlows();
        // 遍历所有入线
        for (SequenceFlow incomingFlow : incomingFlows) {
            FlowElement sourceElement = incomingFlow.getSourceFlowElement();
            if (sourceElement instanceof Gateway) {
                List<SequenceFlow> gatewayIncomes = ((Gateway) sourceElement).getIncomingFlows();
                if (CollUtil.isNotEmpty(gatewayIncomes)) {
                    userTasks.addAll(gatewayIncomes.stream().map(SequenceFlow::getSourceFlowElement).collect(Collectors.toList()));
                }

            } else if (sourceElement instanceof UserTask) {
                userTasks.add(sourceElement);
            }
        }

        return userTasks;
    }

    /**
     * 根据类型获取每条线上的第一个前置节点
     *
     * @param currentElement
     * @param targetClass
     * @return java.util.List<org.activiti.bpmn.model.FlowElement>
     * @author zhonglj
     * @date 2024/12/18 16:00
     **/
    public static List<FlowElement> getPreFirstFlowElement(FlowElement currentElement, Class... targetClass) {
        List<FlowElement> flowElements = new ArrayList<>();
        FlowNode currentNode = (FlowNode) currentElement;
        // 获取当前节点的所有入线
        List<SequenceFlow> incomingFlows = currentNode.getIncomingFlows();
        // 遍历所有入线
        for (SequenceFlow incomingFlow : incomingFlows) {
            FlowElement sourceElement = incomingFlow.getSourceFlowElement();
            if (sourceElement instanceof Gateway) {
                List<SequenceFlow> gatewayIncomes = ((Gateway) sourceElement).getIncomingFlows();
                if (CollUtil.isNotEmpty(gatewayIncomes)) {
                    flowElements.addAll(gatewayIncomes.stream().map(SequenceFlow::getSourceFlowElement).collect(Collectors.toList()));
                }

            } else if (Arrays.asList(targetClass).stream().anyMatch(clazz -> clazz.isInstance(sourceElement))) {
                flowElements.add(sourceElement);
            }
        }

        return flowElements;
    }

    public static List<FlowElement> getPreFirstUserTask(String taskId) {
        ManagementService managementService = SpringUtil.getBean(ManagementService.class);
        TaskService taskService = SpringUtil.getBean(TaskService.class);
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        Process process = managementService.executeCommand(new GetProcessCmd(task.getProcessDefinitionId()));
        FlowElement flowElement = process.getFlowElement(task.getTaskDefinitionKey());
        return getPreFirstUserTask(flowElement);
    }

    public static String formatXml(String unformattedXml) throws Exception {
        try {
            unformattedXml = unformattedXml.trim();
            // 解析未格式化的 XML 字符串
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            Document document = documentBuilder.parse(new ByteArrayInputStream(unformattedXml.getBytes("UTF-8")));

            // 设置输出属性以格式化 XML
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            // 设置缩进量为 2
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");

            // 创建一个输出流来接收格式化后的 XML
            StringWriter writer = new StringWriter();
            transformer.transform(new DOMSource(document), new StreamResult(writer));

            return writer.toString();
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("XML 格式化错误: " + e.getMessage());
        }
    }

    /**
     * 根据当前任务获取下一个符合条件的第一个节点
     *
     * @param task
     * @return org.activiti.bpmn.model.FlowElement
     * @author zhonglj
     * @date 2024/12/4 10:57
     **/
    public static FlowElement getNextFirstNode(Task task) {
        ProcessEngine processEngine = SpringUtil.getBean(ProcessEngine.class);
        ManagementService managementService = processEngine.getManagementService();

        // 获取流程
        Process process = managementService.executeCommand(new GetProcessCmd(task.getProcessDefinitionId()));
        // 创建表达式
        ProcessEngineConfigurationImpl config = (ProcessEngineConfigurationImpl) processEngine.getProcessEngineConfiguration();
        ExpressionManager expressionManager = config.getExpressionManager();

        if (process != null) {
            // 获取当前节点
            FlowElement currentFlowElement = process.getFlowElement(task.getTaskDefinitionKey());

            if (currentFlowElement != null) {
                // 获取下一个节点
                List<SequenceFlow> outgoingFlows = ((FlowNode) currentFlowElement).getOutgoingFlows();

                if (!outgoingFlows.isEmpty()) {
                    for (SequenceFlow out : outgoingFlows) {
                        String conditionExpression = out.getConditionExpression();
                        if (conditionExpression == null) {
                            // 没有条件视为通过
                            return out.getTargetFlowElement();
                        }

                        Expression expression = expressionManager.createExpression(conditionExpression);
                        // 计算表达式的值
                        VariableScope variableScope = getVariableScopeFromTask(task);
                        Object value = expression.getValue(variableScope);
                        boolean pass = ObjectUtil.equal(value, true);
                        if (pass) {
                            // 通过流程条件判断
                            return out.getTargetFlowElement();
                        }
                    }
                }
            }
        }

        return null;
    }

    /**
     * 根据任务获取VariableScope
     *
     * @param task
     * @return org.activiti.engine.delegate.VariableScope
     * @author zhonglj
     * @date 2024-10-19 16:59
     **/
    public static VariableScope getVariableScopeFromTask(Task task) {
        ProcessEngine processEngine = SpringUtil.getBean(ProcessEngine.class);
        RuntimeService runtimeService = processEngine.getRuntimeService();
        TaskService taskService = processEngine.getTaskService();

        // 获取任务的执行上下文
        if (task instanceof TaskEntity) {
            TaskEntity taskEntity = (TaskEntity) task;
            return taskEntity.getExecution();

        } else {
            // 获取任务所属的流程实例 ID
            String processInstanceId = task.getProcessInstanceId();
            // 获取流程实例的执行上下文
            Execution execution = runtimeService.createExecutionQuery()
                    .processInstanceId(processInstanceId)
                    .singleResult();
            if (execution instanceof ExecutionEntity) {
                return (ExecutionEntity) execution;

            } else {
                // 获取任务的执行上下文
                Object object = taskService.getVariable(task.getId(), "execution");
                if (object != null) {
                    return (ExecutionEntity) object;
                } else {
                    return null;
                }

            }
        }
    }


    /**
     * 是否是加签审批
     *
     * @param task
     * @return boolean
     * @author zhonglj
     * @date 2024/12/7 9:30
     **/
    public static boolean isAddSign(Task task) {
        return StrUtil.equals(task.getCategory(), DYNAMIC_PARALLEL_NODE);
    }

    /**
     * 是否连续上级
     *
     * @param task
     * @return boolean
     * @author zhonglj
     * @date 2024/12/7 9:30
     **/
    public static boolean isContinuousSupervisor(Task task) {
        return StrUtil.equals(task.getCategory(), DYNAMIC_SERIAL_NODE);
    }

    /**
     * 是否为多实例
     *
     * @param task
     * @return boolean
     * @author zhonglj
     * @date 2024/12/7 9:30
     **/
    public static boolean isMultiTask(Task task) {
        ManagementService managementService = SpringUtil.getBean(ManagementService.class);
        Process process = managementService.executeCommand(new GetProcessCmd(task.getProcessDefinitionId()));
        FlowElement flowElement = process.getFlowElement(task.getTaskDefinitionKey());
        if (flowElement instanceof UserTask) {
            return ((UserTask) flowElement).hasMultiInstanceLoopCharacteristics();
        }

        return false;
    }

    /**
     * 是否为动态审批链节点
     *
     * @param task
     * @return boolean
     * @author zhonglj
     * @date 2025/2/25 15:58
     **/
    public static boolean isDynamicChain(Task task) {
        return StrUtil.equals(task.getCategory(), DYNAMIC_CHAIN_NODE);
    }

    /**
     * 计算流程表达式
     *
     * @param expressionString
     * @param execution
     * @return java.lang.Object
     * @author zhonglj
     * @date 2025/1/23 10:32
     **/
    public static Object calculateExpression(String expressionString, VariableScope execution) {
        if (StrUtil.isBlank(expressionString) || execution == null) {
            return null;
        }
        ProcessEngine processEngine = SpringUtil.getBean(ProcessEngine.class);
        ProcessEngineConfigurationImpl config = (ProcessEngineConfigurationImpl) processEngine.getProcessEngineConfiguration();
        ExpressionManager expressionManager = config.getExpressionManager();
        // 创建表达式
        Expression expression = expressionManager.createExpression(expressionString);
        // 计算表达式的值
        return expression.getValue(execution);
    }
}
